package com.yhb.channel;

import com.yhb.channel.exception.PayChannelDeadException;
import com.yhb.channel.exception.PayChannelException;
import com.yhb.channel.exception.RemitCardUnboundException;
import com.yhb.channel.exception.TryingBindCardException;
import com.yhb.channel.model.ChannelBalance;
import com.yhb.channel.model.IBankCard;
import com.yhb.channel.model.TryBindCardExtendInfo;
import com.yhb.channel.model.TryPayPayload;
import com.yhb.channel.provider.event.EventType;
import com.yhb.channel.provider.event.PayChannelEventBase;
import com.yhb.channel.utils.ChannelJSONUtil;
import com.yhb.model.User;
import org.jetbrains.annotations.NotNull;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public interface PayChannel {
    /**
     * 入网
     * @param userId 用户id
     * @param province 省份 注意：同下
     * @param city 城市 注意：业务只传统一的一套城市，通道实现时考虑各通道城市名一致性
     * @param area 地区
     * @param card 结算卡
     */
    default void userSignUp(
            String userId,
            String province,
            String city,
            String area,
            IBankCard card,
            String payRate
    ) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法userSignUp未实现");
    }

    /**
     * 修改用户费率
     * @param userId 用户id
     * @param payRate 费率
     */
    default void updatePayRate(String userId, String payRate) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法updatePayRate未实现");
    }

    /**
     * 绑卡
     * @param userId 用户id
     * @param card 结算卡
     * @return uuid，客户端绑卡短信确认时带上
     */
    default String bindCard(
            String userId,
            IBankCard card,
            String clientIP
    ) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法bindCard未实现");
    }

    default String tryBindCard(String userId, IBankCard card,
                               String clientIP) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法tryBindCard(1)未实现");
    }

    /**
     * 尝试绑卡
     * @param userId 用户id
     * @param card 卡
     * @return uuid，客户端绑卡短信确认时带上
     */
    default String tryBindCard(String userId, @NotNull IBankCard card,
                       String clientIP, String payRate,
                       String province, String city, String area
    ) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法tryBindCard(2)未实现");
    }

    /**
     * 绑卡短信确认
     * @param smsCode 短信
     * @param userId 用户id
     * @param smsUUID bindCard返回的uuid
     */
    default String bindCardConfirm(
            String smsCode,
            String userId,
            String smsUUID
    ) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法bindCardConfirm未实现");
    }

    /**
     * 消费 指定地区
     * @param userId 用户id
     * @param payCard 消费卡
     * @param remitCard 消费卡
     * @param orderAmount 订单金额
     * @param userFeeAmount 手续费
     * @return 消费请求结果
     */
    default String pay(
            String userId,
            IBankCard payCard,
            IBankCard remitCard,
            BigDecimal orderAmount,
            BigDecimal userFeeAmount,
            String clientIP,
            String province,
            String city,
            String area
    ) throws PayChannelDeadException, TryingBindCardException {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法pay未实现");
    }

    default String pay(
            String userId,
            IBankCard payCard,
            IBankCard remitCard,
            BigDecimal orderAmount,
            BigDecimal userFeeAmount,
            String clientIP,
            String province,
            String city,
            String area,
            String payRate,
            String token,
            String smsCode,
            String prePayFlowId
    ) throws PayChannelDeadException, TryingBindCardException {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法pay未实现");
    }

    /**
     * 1. 检查 payCard与remitCard是否绑定
     * 2. 对未绑定卡 执行bindCard 抛TryingBindCardException带token token存绑卡信息和消费信息
     * 3. 如都已经绑定 执行pay 返回pay返回的信息
     *
     * @param userId 用户id
     * @param payCard 消费卡
     * @param remitCard 代付卡
     * @param orderAmount 消费金额
     * @param userFeeAmount 手续费
     * @param payRate 消费费率
     * @return 消费单id
     * @throws TryingBindCardException 带有绑卡进行中信息
     */
    default String tryPay(
            String userId,
            IBankCard payCard,
            IBankCard remitCard,
            BigDecimal orderAmount,
            BigDecimal userFeeAmount,
            String clientIP,
            String payRate,
            String province,
            String city,
            String area
    ) throws TryingBindCardException, PayChannelDeadException {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法tryPay未实现");
    }

    /**
     * 根据token取出绑卡信息和消费信息，执行bindCardConfirm，重放tryPay 结束
     * @param userId 用户id
     * @param smsCode 短信验证码
     * @param payload tryPay存下来的payload
     * @return 如果无TryingBindCardException则返回消费单id
     */
    default String verifyPay(
            String userId,
            String smsCode,
            TryPayPayload payload
    ) throws TryingBindCardException, PayChannelDeadException {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法verifyPay未实现");
    }

//    /**
//     * 出款
//     * @param userId 发起出款的用户id
//     * @param card 目标到账银行卡
//     * @param amount 到账金额（单位：分）
//     * @param userFeeAmount 手续费金额（单位：分）
//     * @return 请求结果
//     */
//    default String remit(
//            String userId,
//            IBankCard card, // 收款银行卡
//            BigDecimal amount,
//            BigDecimal userFeeAmount,
//            boolean active, // 是否手动发起的remit
//            TryBindCardExtendInfo tryBindCardExtendInfo
//    ) throws RemitCardUnboundException {
//        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法remit未实现");
//    }


    /**
     * 出款
     * @param userId 发起出款的用户id
     * @param remitCard 目标到账银行卡
     * @param payCard 支付卡
     * @param amount 到账金额（单位：分）
     * @param userFeeAmount 手续费金额（单位：分）
     * @return 请求结果
     */
    default String remit(
            String userId,
            IBankCard remitCard, // 收款卡
            IBankCard payCard, // 支付卡
            BigDecimal amount,
            BigDecimal userFeeAmount,
            boolean active, // 是否手动发起的remit
            TryBindCardExtendInfo tryBindCardExtendInfo
    ) throws RemitCardUnboundException {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法remit未实现");
    }


    /**
     * 重试某笔出款流水
     * @param flowId 流水id
     * @param date 时间
     * @return 新的流水id
     */
    default String retryRemit(
            String flowId,
            Date date
    ) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法retryRemit未实现");
    }

    /**
     * 检查某张卡是否已经绑定（签约不算）
     * @param card 卡
     * @return 是否已经绑定
     */
    default boolean checkIfBound(@NotNull IBankCard card, boolean isRemitCard) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法checkIfBound未实现");
    }
    /**
     * 检查某张卡是否已经绑定（签约不算）
     * @return 是否已经绑定
     */
    default boolean checkPayChannelSmsVerify() {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法checkPayChannelSmsVerify未实现");
    }

    /**
     * 对回调进行操作
     * @param callBackAction 回调类型
     * @param request 回调请求上下文
     * @param response 回调响应上下文
     * @param orderCreateTimestamp 订单创建时间
     */
    default void handleCallback(String callBackAction, HttpServletRequest request, HttpServletResponse response, String orderCreateTimestamp, String info) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "通道方法handleCallback未实现");
    }

    /**
     * 业务方会定时无限触发这个函数 有基于定时的轮询逻辑全放这 默认空实现
     */
    default void tick() {}

    /**
     * 用于解析payload
     */
    default void eventPayloadParser(String payload) {
        PayChannelEventBase payChannelEventBase = ChannelJSONUtil.parseJson(payload, PayChannelEventBase.class);
        EventType type = payChannelEventBase.getType();
        String eventPayload = payChannelEventBase.getPayload();
        this.eventHandler(type, eventPayload);
    }

    /**
     * 用于检查type 分发事件
     */
    default void eventHandler(EventType type, String payload) {}

    /**
     * 获取余额
     * @return 余额
     */
    default ArrayList<ChannelBalance> getBalance(String userId) {
        throw new PayChannelException(ChannelConstant.INNER_WRONG, "不支持查询本通道的余额");
    }
}
